home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pp / pp-6.0 / Chans / smtp / sm_ns.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-12-18  |  9.4 KB  |  475 lines

  1. /* sm_ns.c */
  2.  
  3. # ifndef lint
  4. static char Rcsid[] = "@(#)$Header: /xtel/pp/pp-beta/Chans/smtp/RCS/sm_ns.c,v 6.0 1991/12/18 20:12:19 jpo Rel $";
  5. # endif
  6.  
  7. /*
  8.  * $Header: /xtel/pp/pp-beta/Chans/smtp/RCS/sm_ns.c,v 6.0 1991/12/18 20:12:19 jpo Rel $
  9.  *
  10.  * $Log: sm_ns.c,v $
  11.  * Revision 6.0  1991/12/18  20:12:19  jpo
  12.  * Release 6.0
  13.  *
  14.  */
  15.  
  16.  
  17.  
  18. /* Steve Kille, based on code from Phil Cockcroft and Craig Partridge
  19.     September 1989 */
  20.  
  21.  
  22.  
  23. #include "util.h"
  24. #include "retcode.h"
  25. #include "chan.h"
  26. #include <signal.h>
  27. #ifdef NAMESERVER
  28. #include <isode/internet.h>
  29.  
  30. #include <arpa/nameser.h>
  31. #ifdef XOS_2
  32. #include <arpa/resolv.h>
  33. #else
  34. #include <resolv.h>
  35. #endif
  36.  
  37.  
  38. /* T_UNSPEC was defined only in more recent versions of BIND */
  39.  
  40. #ifdef T_UNSPEC
  41. #define BSD4_3
  42. #define    getshort _getshort
  43. #endif T_UNSPEC
  44.  
  45.  
  46. #ifndef MAXADDR
  47. #define MAXADDR        30
  48. #endif
  49.  
  50. #ifndef MAXADDR_PER
  51. #define MAXADDR_PER    4
  52. #endif
  53.  
  54. #ifndef MAXDATA
  55. #define MAXDATA (4 * PACKETSZ)        /* tcp tried after udp */
  56. #endif
  57.  
  58. #ifndef MAXMX
  59. #define MAXMX    20            /* shouldn't be < MAXADDR */
  60. #endif
  61.  
  62.  
  63. union ansbuf {            /* potentially huge */
  64.     HEADER ab1;
  65.     char ab2[MAXDATA];
  66. }; 
  67.  
  68. union querybuf {        /* just for outbound stuff */
  69.     HEADER qb1;            /* didn't want to clobber stack */
  70.     char qb2[2 * MAXDNAME];
  71. }; 
  72.  
  73. extern char *loc_dom_mta;
  74.  
  75. static  char dn_name[MAXDNAME];
  76.  
  77. static struct hostent hval;
  78. static struct in_addr addrbuf[MAXADDR+1];
  79. static char *addrptrs [MAXADDR+1];
  80. static char addrarray[MAXADDR + 1][sizeof(struct in_addr) + 1];
  81. extern int h_errno;
  82.  
  83. ns_gethost (key, hptr, errstr)
  84. char *key;
  85. struct hostent **hptr;
  86. char    *errstr;
  87. {
  88.     register char *cp;
  89.     register int i, j, n;
  90.     HEADER *hp;
  91.     struct hostent *he;
  92.     union querybuf qbuf;
  93.     union ansbuf abuf;
  94.     u_short type, dsize;
  95.     int pref, localpref;
  96.     int count, mxcount;
  97.     int sawmx;            /* are we actually processing mx's? */
  98.     char *eom;
  99.     char buf[MAXDNAME];        /* for expanding in dn_expand */
  100.     char newkey[MAXDNAME];     /* in case we get a CNAME RR back... */
  101.     struct {            /* intermediate table */
  102.     char *mxname;
  103.     u_short mxpref;
  104.     } mx_list[MAXMX];
  105.  
  106.     int acount;
  107.  
  108.     extern char *ns_skiphdr();
  109.  
  110.     *errstr = '\0';
  111.  
  112.     PP_TRACE (("DNS resolve host %s", key));
  113.  
  114.     for (i = 0; i <= MAXADDR; i++)
  115.     addrptrs[i] = (char *) addrarray[i];
  116.     *hptr = &hval;   
  117.     hval.h_addr_list = addrptrs;
  118.     hval.h_name = key;
  119.     hval.h_aliases = 0;
  120.  
  121.  
  122. restart:
  123.  
  124.     mxcount = 0;
  125.     acount = 0;
  126.     localpref = -1;
  127.     sawmx = 0;
  128.  
  129.     n = res_mkquery(QUERY, key, C_IN, T_MX, (char *)0, 0, (char *)0,
  130.     (char *)&qbuf, sizeof(qbuf));
  131.  
  132.     /* what else can we do? */
  133.     if (n < 0)
  134.     {
  135.     (void) sprintf (errstr, "Unable to send query");
  136.     PP_NOTICE (("No answers from res_mkquery"));
  137.     return(RP_NO);
  138.     }
  139.  
  140.     PP_TRACE (("ns_gethost: sending ns query (%d bytes)",n));
  141.  
  142.     n = res_send((char *)&qbuf,n,(char *)&abuf, sizeof(abuf));
  143.  
  144.     if (n < 0)
  145.     {
  146.     (void) sprintf (errstr, "Unable to contact the DNS");
  147.     PP_SLOG (LLOG_EXCEPTIONS, "res_send", ("DNS resolver error: "));
  148.     return(RP_TIME);
  149.     }
  150.  
  151.     hp = (HEADER *)&abuf;
  152.  
  153.     if (hp->rcode != NOERROR) 
  154.     return(ns_error(hp, errstr, key));
  155.  
  156.  
  157.     if (ntohs(hp->ancount) == 0)
  158.     {
  159.     mxcount = 1;
  160.     mx_list[0].mxname = strdup(key);
  161.     mx_list[0].mxpref = 0;
  162.     goto doaddr;
  163.     }
  164.  
  165.     /* read MX list */
  166.     sawmx = 1;
  167.     count = ntohs(hp->ancount);
  168.  
  169.  
  170.     /* skip header */
  171.     eom = ((char *)&abuf) + n;
  172.     if ((cp = ns_skiphdr((char *)&abuf, hp, eom))==0)
  173.     {
  174.     (void) sprintf (errstr, "No useful answers to query of %s", key);
  175.     PP_NOTICE (("ns_gethost: no useful answers to query"));
  176.     return(RP_TIME);
  177.     }
  178.  
  179.     PP_TRACE (("ns_gethost: %d answers to query",count));
  180.     for (i = 0; i < MAXMX; i++)
  181.         mx_list[i].mxname = NULL;
  182.  
  183.     while ((cp < eom) && (count--))
  184.     {
  185.     n = dn_expand((char *)&abuf,eom, cp, buf, sizeof(buf));
  186.     if (n < 0)
  187.         goto quit;
  188.  
  189.     cp += n;
  190.     type = getshort(cp);
  191.     /* get to datasize */
  192.     cp += (2 * sizeof(u_short)) + sizeof(u_long);
  193.     dsize = getshort(cp);
  194.     cp += sizeof(u_short);
  195.  
  196.     /*
  197.      * is it an MX ? 
  198.      * it could be a CNAME
  199.      */
  200.  
  201.     if (type == T_CNAME)
  202.     {
  203.         if (key == newkey) {
  204.         /* CNAME -> CNAME: illegal */
  205.         PP_LOG (LLOG_EXCEPTIONS, ("recursive CNAME on MX query of %s",
  206.                       key));
  207.         goto quit;
  208.         }
  209.         PP_TRACE (("ns_gethost: CNAME answer to MX query"));
  210.         n = dn_expand((char *)&abuf,eom, cp, newkey, sizeof(newkey));
  211.         cp += dsize;
  212.         if (n < 0)
  213.         continue;    /* pray? */
  214.         PP_TRACE (("ns_gethost: `%s` -> `%s` (new query)",key,newkey));
  215.         key = newkey;
  216.         goto restart;
  217.     }
  218.  
  219.     if (type != T_MX)
  220.     {
  221.         PP_NOTICE (("ns_gethost: RR of type %d in response",type));
  222.         cp += dsize;
  223.         continue;       /* keep trying */
  224.     }
  225.  
  226.     pref = getshort(cp);
  227.     cp += sizeof(u_short);
  228.  
  229.     n = dn_expand((char *)&abuf,eom, cp, buf, sizeof(buf));
  230.     if (n < 0)
  231.         goto quit;
  232.  
  233.     cp += n;
  234.  
  235.     /* is it local? */
  236.     if ((lexequ(loc_dom_mta, buf) == 0) &&
  237.         ((localpref < 0) || (pref < localpref)))
  238.     {
  239.         localpref = pref;
  240.         for(i=(mxcount-1); i >= 0; i--)
  241.         {
  242.         if (mx_list[i].mxpref < localpref)
  243.             break;
  244.  
  245.         (void) free(mx_list[i].mxname);
  246.         mxcount--;
  247.         }
  248.         continue;
  249.     }
  250.  
  251.     /* now, see if we keep it */
  252.     if ((localpref >= 0) && (pref >= localpref))
  253.         continue;
  254.  
  255.     /*  find where it belongs */
  256.     for(i=0; i < mxcount; i++)
  257.         if (mx_list[i].mxpref > pref)
  258.         break;
  259.  
  260.     /* not of interest */
  261.     if (i == MAXMX)
  262.         continue;
  263.  
  264.     /* shift stuff to make space */
  265.     for(j= min(mxcount,MAXMX-1); j > i; j--)
  266.     {
  267.         if (j == (MAXMX-1) && mx_list[j].mxname)
  268.         (void) free(mx_list[j].mxname);
  269.  
  270.         mx_list[j].mxname = mx_list[j-1].mxname;
  271.         mx_list[j].mxpref = mx_list[j-1].mxpref;
  272.     }
  273.  
  274.     mx_list[i].mxname = strdup(buf);
  275.     mx_list[i].mxpref = pref;
  276.  
  277.     if (mxcount < MAXMX)
  278.         mxcount ++;
  279.     }
  280.  
  281.     /*
  282.      * should read additional RR section for addresses and cache them
  283.      * but let's hold on that.
  284.      */
  285.  
  286. doaddr:
  287.     /* now build the address list */
  288.     
  289.  
  290.     PP_TRACE (("ns_gethost: using %d mx hosts",mxcount));
  291.  
  292.     for(i=0,j=0; (i < mxcount) && (j < MAXADDR); i++)
  293.     {
  294.     /*
  295.      * note that gethostbyname() is slow -- we should cache so
  296.      * we don't ask for an address repeatedly
  297.      */
  298.  
  299.     he = gethostbyname(mx_list[i].mxname);
  300.  
  301.     if (he == 0)
  302.     {
  303.         PP_NOTICE (("ns_gethost: no addresses for %s",
  304.             mx_list[i].mxname));
  305.         /* nope -- were trying special case and no address */
  306.         if ((!sawmx) && (h_errno != TRY_AGAIN)) {
  307.         sprintf (errstr, "No nameserver addresses for %s", 
  308.                 mx_list[i].mxname);
  309.         return(RP_NO);
  310.         }
  311.  
  312.         continue;
  313.     }
  314.  
  315.     if (j == 0)
  316.      {
  317.         hval.h_length = he -> h_length;
  318.         hval.h_addrtype = he -> h_addrtype;
  319.     }
  320.  
  321.     for(n=0; (j < MAXADDR) && (n < MAXADDR_PER); n++, j++)
  322.     {
  323.         if (he->h_addr_list[n] == 0)
  324.         break;
  325.  
  326.         bcopy(he->h_addr_list[n],hval.h_addr_list[j],sizeof(struct in_addr));
  327.  
  328.     }
  329.     PP_TRACE (("ns_gethost: %d addresses saved for %s",
  330.         n, mx_list[i].mxname));
  331.     }
  332.     acount = j;
  333.     hval.h_addr_list[j] = 0; 
  334.  
  335. quit:
  336.     for(i=0; i < mxcount; i++)
  337.     (void) free(mx_list[i].mxname);
  338.  
  339.     if (acount == 0) {
  340.     (void) sprintf (errstr, "Nameserver lookup of %s: No address found",                 key);
  341.     PP_NOTICE (("ns_gethost: No addresess derived from MX query"));
  342.     }
  343.     else
  344.     PP_NOTICE (("DNS resolves %s to %d address%s", key, acount,
  345.             acount > 1 ? "es" : ""));
  346.     /* if localpref is set, then we got an answer */
  347.     return (acount == 0 ? (localpref < 0 ? RP_TIME : RP_NO) : RP_OK);
  348. }
  349.  
  350. static char *prcode (n)
  351. int n;
  352. {
  353.     static char tbuf[40];
  354.  
  355.     switch (n) {
  356.         case NOERROR:
  357.         return "No Error (NOERROR)";
  358.  
  359.         case FORMERR:
  360.         return "Format Error (FORMERR)";
  361.  
  362.         case SERVFAIL:
  363.         return "Server failure (SERVFAIL)";
  364.  
  365.         case NXDOMAIN:
  366.         return "Non existant host/domain (NXDOMAIN)";
  367.  
  368.         case NOTIMP:
  369.         return "Not implemented (NOTIMP)";
  370.  
  371.         case REFUSED:
  372.         return "Query Refused (REFUSED)";
  373.  
  374.         default:
  375.         (void) sprintf (tbuf, "Unknown code %d", n);
  376.         return tbuf;
  377.     }
  378. }
  379.         
  380. /*
  381.  * figure out proper error code to return given an error
  382.  */
  383.  
  384. static
  385. ns_error(hp, errstr, key)
  386. register HEADER *hp;
  387. char    *errstr;
  388. char *key;
  389. {
  390.  
  391.     PP_NOTICE (("DNS error: %s",prcode(hp->rcode)));
  392.     switch (hp->rcode)
  393.     {
  394.     case NXDOMAIN:
  395.         (void) sprintf (errstr, "Nameserver error for %s: %s", key,
  396.                 prcode (hp -> rcode));
  397.         return(RP_NO); /* even if not authoritative */
  398.  
  399.     case SERVFAIL:
  400.         return(RP_TIME);
  401.  
  402.     default:
  403.         break;
  404.     }
  405.     (void) sprintf (errstr, "Nameserver error for %s: %s", 
  406.             key, prcode(hp->rcode));
  407.     return(RP_NO);
  408. }
  409.  
  410. /*
  411.  * skip header of query and return pointer to first answer RR.
  412.  */
  413.  
  414. static
  415. char *ns_skiphdr(answer, hp, eom)
  416. char *answer;
  417. HEADER *hp;
  418. register char *eom;
  419. {
  420.     register int qdcount;
  421.     register char *cp;
  422.     register int n;
  423.     char tmp[MAXDNAME];
  424.  
  425.     qdcount = ntohs(hp->qdcount);
  426.  
  427.     cp = answer + sizeof(HEADER);
  428.  
  429.     while ((qdcount-- > 0) && (cp < eom))
  430.     {
  431.     n = dn_expand(answer,eom,cp,tmp,sizeof(tmp));
  432.     if (n < 0)
  433.         return(0);
  434.     cp += (n + QFIXEDSZ);
  435.     }
  436.  
  437.     return((cp < eom)? cp : 0);
  438. }
  439.  
  440. /*
  441.  * routine to set the resolver timeouts
  442.  * takes maximum number of seconds you are willing to wait
  443.  */
  444.  
  445. ns_settimeo(ns_time)
  446. int     ns_time;
  447. {
  448.     static int called = 0;
  449.     static struct state oldres;
  450.  
  451.     if ((_res.options & RES_INIT) == 0)
  452.         res_init ();
  453.  
  454.     /* always start afresh */
  455.     if (called)
  456.     {
  457.     bcopy((char *)&oldres,(char *)&_res,sizeof(oldres));
  458.     }
  459.     else
  460.     {
  461.     called = 1;
  462.     bcopy((char *)&_res,(char *)&oldres,sizeof(oldres));
  463.     }
  464.  
  465.     /*
  466.      * bind uses an exponential backoff
  467.      */
  468.  
  469.     _res.retrans = ns_time >> _res.retry;
  470.  
  471.     PP_TRACE (("ns_timeo: servers(%d), retrans(%d), retry(%d)",
  472.     _res.nscount, _res.retrans, _res.retry));
  473. }
  474. #endif
  475.